import cache
import models
import variable


def option_parse(args, strict_option_list=None, map_data=None):
    """
    选项解析算法实现(2019年12月20日 星期五)/函数式
    :param args: 数组参数
    :param strict_option_list: 严格数据列表
    :param map_data: option 映射键值
    :return: (options, cmds, object_data)
    """
    # 选项队列
    options = []
    # 数据对象
    object_data = {}
    # 命令
    cmds = []

    args_len = len(args)

    # 键存在标记
    last_key_mark = ''

    # 严格检查
    def strict_check_fn(copt):
        if copt and strict_option_list is not None and isinstance(strict_option_list, list):
            if not (copt in strict_option_list):
                raise Exception(f'The option <{copt}> is not allowed.')

    # 多选择检查
    def strict_check_list_fn(lists):
        if lists and strict_option_list is not None and isinstance(strict_option_list, list):
            if isinstance(lists, list):
                for vlst in lists:
                    strict_check_fn(vlst)

    for i in range(0, args_len):
        arg = args[i].strip()
        arg_len = len(arg)

        if arg_len > 3:
            if arg[:2] == '--':
                arg = arg[2:]

                # --key=value
                if '=' in arg:
                    idx = arg.find('=')
                    cur_key = arg[:idx]
                    strict_check_fn(cur_key)

                    last_key_mark = ''

                    object_data[cur_key] = arg[idx + 1:]
                    options.append(cur_key)

                # --key
                else:
                    strict_check_fn(arg)
                    options.append(arg)
                    last_key_mark = arg

                continue

        if arg[:1] == '-':
            arg = arg[1:]
            tmp_list = list(arg)
            strict_check_list_fn(tmp_list)

            # list 合并
            options = options + tmp_list

            last_key_mark = options[len(options) - 1]

        elif last_key_mark != '':
            # 已经存在值，认为时数组
            if last_key_mark in object_data:
                if isinstance(object_data[last_key_mark], list):
                    object_data[last_key_mark].append(arg)
                else:
                    tmp_value = object_data[last_key_mark]
                    object_data[last_key_mark] = []
                    object_data[last_key_mark].append(tmp_value)
                    object_data[last_key_mark].append(arg)

            else:
                object_data[last_key_mark] = arg

        # 将命令行放到命令列表内
        elif len(options) == 0 and len(object_data) == 0:
            cmds.append(arg)

    return options, cmds, object_data


class Args:
    def __init__(self, args):
        self.cmd = None
        self.quque = []
        self.vdata = {}  # 数据
        self.options = []  # 选项
        self.cmds = []
        self._parseCmd(args)
        self.sub_cmd = None

        self._after_parse()

    def _parseCmd(self, args):
        args = args.split(' ')
        newData = []
        cmd = None

        for a in args:
            a = a.strip()
            if a == '':
                continue

            if not cmd:
                cmd = a
            else:
                newData.append(a)

        self.cmd = cmd
        self.quque = newData
        # 解析数据
        options, cmds, object_data = option_parse(newData)

        self.vdata = object_data
        self.options = options
        self.cmds = cmds

    def _after_parse(self):
        cmds = self.cmds
        if len(cmds) > 0:
            self.sub_cmd = cmds[0]

    # 路由器
    def router(self, app):
        app.args = self
        if self.cmd and hasattr(app, self.cmd):
            action = getattr(app, self.cmd)
            if callable(action):
                action()
                return True

        return False

    # 获取参数
    def get_data(self, key, dval=None):
        if key in self.vdata:
            return self.vdata[key]
        return dval


# 缓存数据
CacheData = {}
CurrentCacheKey = None  # 当前缓存值


# 通用数据对象
class CommandBase:
    def __init__(self):
        self.args = None
        self.name = ''  # 命令
        self.exit = 'exit'  # 退出命令


# 我的命令
class MyCommand(CommandBase):
    def help(self):
        """
        帮助
        :return:
        """
        cmds = self.args.cmds
        help_cmd = cmds[0] if len(cmds) > 0 else None

        if help_cmd:
            if help_cmd == 'create':
                print(' create    连接数据库；支持参数 host=localhost,user=root,pswd,dbname,charset=utf8,+name')
                print('   . 连接数据库后，会缓存连接信息以及表结构')
                print('   . 支持传入: 缓存命令，以及连接配置参数')
            elif help_cmd == 'diff':
                print(' diff <db1> <db2>   数据对比工具')
                print('   . 以 db1 与 db2 进行结构对比')
            else:
                print(f' {help_cmd} 命令无详情或者不存在')
        else:
            print(' create    连接数据库；支持参数 host=localhost,user=root,pswd,dbname,charset=utf8,+name')  # 对比方式 git 增删改
            print(' diff <db1> <db2>   数据对比')  # 对比方式 git 增删改
            print(' patch <db1> <db2>  生成补丁文件sql')  # 对比方式 git 增删改
            print(' exit   退出系统')
            print(' list               查看缓存数据库连接列表')
            print(' help [<command>]   查看帮助消息')
            print()
            print(' option 格式: --key=value')

    def create(self):
        """
        创建连接
        :return:
        """
        global CurrentCacheKey
        global CacheData
        vdata = self.args.vdata
        sub_cmd = self.args.sub_cmd
        if sub_cmd:
            ret = models.MysqlModel.create_by_name(sub_cmd)
            if ret:
                print(f' {sub_cmd} 连接失败，{ret}')
            else:
                print(f' {sub_cmd} 连接成功，且以刷新缓存')
        else:
            mm = models.MysqlModel(vdata)
            if mm.msg:
                print(mm.msg)
            else:
                # 数据缓存
                dc = mm.dbCacheObject
                if dc:
                    CurrentCacheKey = dc.name
                    CacheData[dc.name] = dc
                print(' 已生成结构化数据缓存')

    def diff(self):
        """
        数据库对比工具
        :return:
        """
        cmds = self.args.cmds
        if len(cmds) == 2:
            dc = cache.DiffCache()
            dc.diff(cmds[0], cmds[1])
        else:
            print('请求参数无效 $ name1 name2')

    def list(self):
        """
        连接列表
        :return:
        """
        clist = cache.DatabaseCache.list()
        if len(clist) > 0:
            for c in clist:
                print(f'  . {c}')
        else:
            print('无数据连接缓存')

    # @todo @try 使用命令行默认打开文件
    def patch(self):
        """
        sql生成差别补丁
        :return:
        """
        cmds = self.args.cmds
        if len(cmds) == 2:
            dc = cache.DiffCache()
            dc.patch(cmds[0], cmds[1])
        else:
            print('请求参数无效 $ name1 name2')


class Command:
    def __init__(self):
        global CurrentCacheKey
        myapp = MyCommand()
        print(f"  欢迎使用该应用被<{variable.AUTHOR}>创造！")
        print("   一个轻量级的mysql数据库对比工具")
        print(f"  version: {variable.VERSION}/{variable.RELEASE}")
        while True:
            tip = '$ '
            if CurrentCacheKey:
                tip = f'({CurrentCacheKey})$ '
            args = input(tip)
            app = Args(args)

            if args == 'exit':
                break

            if not app.router(myapp):
                print(f'{app.cmd} 不存在!')
                continue
